package al.xc.common.util;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.util.*;

/**
 * xml 和 map 互转
 */
public class XmlMapUtils {

    /**
     * 将xml转为map
     * @param xml
     * @return
     */
    public static Map<String, Object> fromXml(String xml) {
        if (null == xml || xml.length() == 0) {
            return null;
        }

        Document document = null;
        try {
            document = DocumentHelper.parseText(xml);
        } catch (DocumentException ex) {
            return null;
        }
        if (null == document) {
            return null;
        }
        return elementToMap(document.getRootElement());
    }

    /**
     * 将map转为xml
     * @param map
     * @param root
     * @param addCDATA
     * @return
     */
    public static String fromMap(Map<String, Object> map, String root, boolean addCDATA) {
        Map<String, Object> rootMap = null;
        if (null != root) {
            rootMap = (Map<String, Object>)map.get(root);
            if (null == rootMap) {
                rootMap = map;
            }
            Document document = DocumentHelper.createDocument();
            document.addElement(root);
            iteratorXml(document.getRootElement(), root, rootMap, addCDATA);
            return document.getRootElement().asXML();
        } else {
            rootMap = map;
            Document document = DocumentHelper.createDocument();
            document.addElement("xml");
            iteratorXml(document.getRootElement(), null, rootMap, addCDATA);
            return document.getRootElement().asXML();
        }
    }

    /**
     * 转 map 子方法
     * @param root
     * @return
     */
    private static Map<String, Object> elementToMap(Element root) {
        Map<String, Object> result = new HashMap<>();

        for (Element element : root.elements()) {
            String name = element.getName();

            Object value = null;
            if (element.elements().size() == 0) {
                value = element.getTextTrim();
            } else {
                value = elementToMap(element);
            }

            Object object = result.get(name);
            if (null == object) {
                result.put(name, value);
            } else {
                if (!(object instanceof List)){
                    result.remove(object);
                    List<Object> list = new ArrayList<>();
                    list.add(object);
                    list.add(value);
                    result.put(name, list);
                } else {
                    ((List)object).add(value);
                }
            }
        }
        return result;
    }

    /**
     * 转 xml 子方法
     * @param element
     * @param root
     * @param map
     */
    private static void iteratorXml(Element element, String root, Map<String, Object> map, boolean addCDATA) {
        Element subElement = null;
        if (null != root) {
            subElement = element.addElement(root);
        } else {
            subElement = element;
        }
        Set<String> set = map.keySet();
        for (Iterator<String> itr = set.iterator(); itr.hasNext();) {
            String key = itr.next();
            Object object = map.get(key);
            if (object instanceof List) {
                List<Object> list = (List<Object>)object;
                for (Object value : list) {
                    if (value instanceof Map) {
                        iteratorXml(subElement, key, (Map)value, addCDATA);
                    } else {
                        if (addCDATA) {
                            subElement.addElement(key).addCDATA(value.toString());
                        } else {
                            subElement.addElement(key).addText(value.toString());
                        }
                    }
                }
            } else if(object instanceof Map) {
                iteratorXml(subElement, key, (Map)object, addCDATA);
            } else {
                if (addCDATA) {
                    subElement.addElement(key).addCDATA(object.toString());
                } else {
                    subElement.addElement(key).addText(object.toString());
                }
            }
        }
    }
}
